//888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
//
//	File		 :	ParseFASTA.cpp
//	Contents	 :	Source file for class ParseMSF
//	Creation Date:	22/12/1999
//	Last Modified:	23/12/1999	lg
//	tabs width	 :	every 4 characters
//
//888888888888888888888888888888888888888888888888888888888888888888888888888888888888888


#include <stdexcept>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <iostream>
#include <cassert>
#include "parsefasta.h"
#include "msfresource.h"
#ifdef _MFC_VER	// mfc specific code to follow
#include "../common/mfc_stdcpp.h"
#else
#include "../common/nonmfc_stdcpp.h"
#endif



//_______________________________________________________________________________________
//
//	Constructor
//
//		Just initializes variables
//		Last modified: lg 23/12/1999
//
//_______________________________________________________________________________________
CParseFASTA::CParseFASTA(CParseMessageCallback& callback)
	: CParse(callback, IDS_ERR_INVALID_FASTAFILE, IDS_ERR_FASTAFILE)
	//("This is not a valid FASTA file.\r\n");
	//"There is an error in the FASTA file.\r\n"
{
}


//_______________________________________________________________________________________
//
//	Do Parse
//
//		Parse FASTA_Stream into Sequences of amino acids
//		Input		: msf_Stream
//		Output		: outputSequence
//		Last modified: lg 23/12/1999
//
//_______________________________________________________________________________________
bool CParseFASTA::DoParse(char* const data_Stream, CSequence& outputSequence)
{
	assert(data_Stream);
	assert(outputSequence.FormatType == CSequence::eFASTA);

	// clear all variables
	LineNumber = 0;
	outputSequence.Clear();


	// get line number for diagnostic purposes
	// save Data_Stream variable for convenience
	char* nextLine = data_Stream;
	Data_Stream = 0;

	while (nextLine)
	{
		// get next line
		PrepareNextLine(nextLine, Data_Stream, LineNumber);

		// Get accession code (name) starting with '>'
		if (*Data_Stream == '>')
		{
			// skip past initial non-space whitespace
			while (*Data_Stream && iswhitespace(*Data_Stream))
				++Data_Stream;

			// nothing by a '>' !!!
			if (!*Data_Stream)
				continue;

			char* end = Data_Stream + 1;

			// skip to next whitespace
			while (*end && isnonwhitespace(*end))
				++end;
			outputSequence.Names.push_back(std::string(Data_Stream, end - Data_Stream));
			outputSequence.Data.push_back("");

		}

		// This is Data
		else
		{
			// The first line of text should be a sequence ID (Name) and preceded by a ">"
			// (greater than sign).
			if(ParseErr(outputSequence.Count() == 0,
						IDS_ERR_FASTA_DATANONAME, LineNumber))
				return false;


			// skip past initial whitespace
			SkipSpaces(Data_Stream);

			// skip to next whitespace
			char* end = Data_Stream;
			SkipToSpace(end);

			// nothing on this line
			if (end == Data_Stream)
				continue;

			// add all to current sequence
			outputSequence.Data[outputSequence.Count()-1].append(Data_Stream, end - Data_Stream);

			// skip past initial whitespace
			SkipSpaces(end);

			// warn if any text after whitespace
			// "Line %d contains whitespace such as tabs etc. Text after this will be discarded.\r\n("%s")."
			if (ParseWarn(*end != 0, IDS_POST_WHITESPACE_DISCARD, LineNumber, end))
				return false;

		}
	}

	// test if all names are unique
	unsigned cnt = outputSequence.CheckDuplicateNamesCnt();
	// "All Sequence Names must be unique. There were %d duplicate(s)."
	if (ParseErr(cnt != 0, IDS_ERR_MSF_DUPLICATENAMES, -(int)cnt))
		return false;


	// make sure there is data in some of the sequences
	unsigned lenCnt = 0;
	for (int i = outputSequence.Count() - 1; i >= 0; --i)
		lenCnt += outputSequence.Data[i].length();

	// "No sequence data could be found in any of the %s lines."
	if(ParseErr(lenCnt ==0, IDS_ERR_FASTA_NODATA, LineNumber))
		return false;

	// make sure all sequences contain data
	int index = outputSequence.CheckEmptyData();
	// "One or more of the sequences names (e.g. %s) were not "
	// "followed by data lines and must be discarded."
	if (ParseWarn(index == -1,
			IDS_ERR_FASTA_NAMENODATA, -(int)LineNumber,	outputSequence.Names[index].c_str()))
		return false;

	// remove sequences with no data
	outputSequence.RemoveEmptyData();

	if (!outputSequence.CheckLengthsEq())
	{
		ParseErrLength(outputSequence);
		return false;
	}

	outputSequence.Len = outputSequence.Data[0].size();

	return true;
}

